package blt.app_core.http;

import android.text.TextUtils;



import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;

import blt.app_core.http.callback.DefaultCallback;
import blt.app_core.http.callback.HttpCallback;
import blt.app_core.http.request.ForwardRequestBody;
import blt.app_core.http.request.ForwardResponseBody;
import blt.app_core.utils.FileHelper;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;

/**
 * Created by Administrator on 2016/12/13.
 */
public class HttpCore {
    private static final String TAG = "HttpCore";
    public static final int METHOD_GET = 1;
    public static final int METHOD_POST = 2;

    protected int mBufferSize = 2048;
    protected OkHttpClient okHttpClient;
    protected RequestBuilder mBuilder;

    public HttpCore(RequestBuilder builder) {
        okHttpClient = new OkHttpClient();
        mBuilder = builder;
    }

    public Interceptor interceptToProgressResponse() {
        return new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Response response = chain.proceed(chain.request());
                ResponseBody body = new ForwardResponseBody(response.body());
                return response.newBuilder()
                        .body(body)
                        .build();
            }
        };
    }

    /**
     * 异步get
     */
    protected void executeGetAsync(HttpCallback callback, String url, Object tag, Map<String, String> strParams) {
        Request.Builder builder = mBuilder.builderGet(url, strParams);
        if (builder == null) {
            callFailure(callback, null, null, null);
            return;
        }
        async(builder, tag, callback);
    }

    public final void async(Request.Builder builder, Object tag, HttpCallback callback) {
        setTag(builder, tag);
        deliveryResult(builder.build(), callback);
    }


//    private void callFailure(HttpCallback<?> callback, Request req, Response response, Exception e) {
//        if (callback != null) {
//            callback.dispatchFailure(req, response, e);
//        }
//    }

//    @SuppressWarnings("unchecked")
//    public void callSuccess(final HttpCallback callback, final Object object, final int code) {
//        if (callback == null)
//            return;
//        callback.dispatchSuccess(object, code);
//    }

    /**
     * --------post---------
     **/
    protected void executePost(String url, Map<String, String> params, HttpCallback callback, Object tag) {
        Request.Builder builder = mBuilder.builderPost(url, params);
        if (builder == null) {
            return;
        }
        asyncPost(builder, tag, callback);
    }

    private void asyncPost(Request.Builder builder, Object tag, HttpCallback callback) {
        setTag(builder, tag);
        deliveryResult(builder.build(), callback);
    }

    private void deliveryResult(Request request, HttpCallback callback) {
        final HttpCallback<?> resCallBack = callback == null ? new DefaultCallback() : callback;
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                callFailure(resCallBack, call.request(), null, e);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                try {
                    String string = response.body().string();
                    boolean haveValue = !TextUtils.isEmpty(string);
                    if (haveValue) {
                        callSuccess(resCallBack, string, response.code());
                    }
                } catch (Exception e) {
                    callFailure(resCallBack, call.request(), null, e);
                } catch (Throwable t){
                    callFailure(resCallBack, call.request(), null, new RuntimeException(t));
                }

            }
        });
    }

    protected Call executeUploadAsync(String url, String path, Map<String, String> params, Object tag, HttpCallback callback) {
        Request.Builder builder = mBuilder.builderPost(url, path, params);
        if (builder == null) {
            return null;
        }
        return uploadAsync(builder, tag, callback);
    }

    private Call uploadAsync(Request.Builder builder, Object tag, HttpCallback callback) {
        setTag(builder, tag);
        return deliveryUploadResult(builder.build(), callback);
    }

    private Call deliveryUploadResult(Request request, HttpCallback callback) {
        final HttpCallback<?> resCallBack = callback == null ? new DefaultCallback() : callback;
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                try {
                    String string = response.body().string();
                    boolean haveValue = !TextUtils.isEmpty(string);
                    if (haveValue) {
                        callSuccess(resCallBack, string, response.code());
                    }
                } catch (Throwable t) {
                    callFailure(resCallBack, call.request(), null, null);
                }
            }

            @Override
            public void onFailure(Call call, IOException e) {
                callFailure(resCallBack, call.request(), null, e);
            }
        });
        return call;
    }


    /**
     * =======   download   =======
     **/
    public void executeDownloadAsync(HttpCallback<File> callback, String url, File outFile, Object tag, int method, Map<String, String> params) {
        Request.Builder builder;
        if (method == METHOD_POST) {
            builder = mBuilder.builderPost(url, null);
        } else {
            builder = mBuilder.builderGet(url, params);
        }
        if (builder == null) {
            return;
        }
        downloadAsync(builder, outFile, tag, callback);
    }

    private void downloadAsync(Request.Builder builder, final File outFile, Object tag, final HttpCallback<File> callback) {
        setTag(builder, tag);
        Request request = builder.build();

        try {
            //生成一个文件用来读取下载的流
            FileHelper.makeFile(outFile);
            final FileOutputStream out = new FileOutputStream(outFile);
            deliveryAsyncResult(request, new StreamCall() {
                @Override
                public OutputStream getOutputStream() {
                    return out;
                }

                @Override
                public void onSuccess(int code) {
                    callSuccess(callback, outFile, code);
                }
            }, callback);


        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    protected void deliveryAsyncResult(final Request request, final StreamCall streamCall, final HttpCallback<?> callback) {
        final HttpCallback<?> resCallBack = callback == null ? new DefaultCallback() : callback;

        //Call start
        callStart(resCallBack, request);

        okHttpClient.newCall(request).enqueue(new Callback() {
            public void onFailure(Call call, IOException e) {
                callFailure(resCallBack, call.request(), null, e);
                callFinish(resCallBack);
            }

            public void onResponse(Call call, Response response) {
                OutputStream out = streamCall.getOutputStream();
                InputStream in = null;
                byte[] buf = new byte[mBufferSize];
                try {

                    ResponseBody body = response.body();
                    bindResponseProgressCallback(request.body(), body, callback);

                    in = body.byteStream();

                    int size;
                    while ((size = in.read(buf)) != -1) {
                        out.write(buf, 0, size);
                        out.flush();
                    }
                    // On success
                    streamCall.onSuccess(response.code());
                } catch (Exception e) {
                    callFailure(resCallBack, response.request(), response, e);
                } finally {
                    okhttp3.internal.Util.closeQuietly(in);
                    okhttp3.internal.Util.closeQuietly(out);
                }
                callFinish(resCallBack);
            }
        });
    }

    private void bindResponseProgressCallback(RequestBody requestBody, ResponseBody responseBody, HttpCallback<?> callback) {
        if (requestBody instanceof ForwardRequestBody) {
            if (((ForwardRequestBody) requestBody).getListener() != null) {
                return;
            }
        }
        if (responseBody instanceof ForwardResponseBody) {
            ((ForwardResponseBody) responseBody).setListener(callback);
        }
    }

    protected void callSuccess(final HttpCallback callback, final Object object, final int code) {
        if (callback != null)
            callback.dispatchSuccess(object, code);
    }

    protected void callStart(final HttpCallback<?> callback, final Request request) {
        if (callback != null)
            callback.dispatchStart(request);
    }

    protected void callFailure(final HttpCallback callback, final Request request, final Response response, final Exception e) {
        if (callback != null) {
            callback.dispatchFailure(request, response, e);
        }
    }

    protected void callFinish(final HttpCallback<?> callback) {
        if (callback != null)
            callback.dispatchFinish();
    }

    /**
     * ============Main call in this============
     */
    protected Request.Builder setTag(Request.Builder builder, Object tag) {
        if (tag != null) {
            builder.tag(tag);
        }
        return builder;
    }






}
